package ua.kpi_java_training.zabrodskyi.module1;

import java.util.Scanner;

/**
 * Class is designed for input two numbers from the keyboard and output to the
 * console result of their division in a column.
 * 
 * @version 1.10 28 May 2013
 * @author Maksym Zabrodskyi
 */

public class DividerInColumn {
	static int dividend = 0;
	static int divisor = 1;
	static int iteration = 0;
	static int counterSpases = 0;
	static String dividendStr = "";
	static String resultDivision = "";

	/**
	 * In this method other methods by means of which get the dividend and
	 * divisor from the console, perform long division and output results to the
	 * console.
	 * 
	 * @param args
	 *            - not used.
	 */
	public static void main(String[] args) {
		Scanner scanner = new Scanner(System.in);
		dividend = getNumber(scanner, "Please enter a dividend:");
		divisor = getDivisor(scanner, "Please enter a divisor:");
		dividendStr = Integer.toString(Math.abs(dividend));
		doDivision();
		printHeaderColumnToConsole(dividend, divisor);
		printBodyColumnToConsole(resultDivision);
	}

	/**
	 * Gets a number that the user enters in the console.
	 * 
	 * @param scanner
	 *            - object of the class Scanner, which allows the user to read
	 *            values.
	 * @param message
	 *            - welcome message to be displayed to the user on the screen.
	 * @return integer number.
	 */
	public static int getNumber(Scanner scanner, String message) {
		int number = 0;
		while (true) {
			try {
				System.out.println(message);
				String text = scanner.nextLine();
				number = Integer.parseInt(text);
				break;
			} catch (Exception e) {
				System.out.println("Error: incorrect data.");
			}
		}
		return number;
	}

	/**
	 * Gets a divisor. The divisor is checked that it can not be zero.
	 * 
	 * @param scanner
	 *            - object of the class Scanner, which allows the user to read
	 *            values.
	 * @param message
	 *            - welcome message to be displayed to the user on the screen.
	 * @return integer number.
	 */
	public static int getDivisor(Scanner scanner, String message) {
		int divisor = 1;
		do {
			divisor = getNumber(scanner, message);
			if (isDivisorZero(divisor)) {
				System.out.println("Error: divisor must not be zero.");
			}
		} while (isDivisorZero(divisor));
		return divisor;
	}

	/**
	 * Performs the process of long division for dividing two numbers. In the
	 * method used by the class variables and the result of long division is
	 * saved in the string.
	 */
	public static void doDivision() {
		int numerator = 0;
		int whole = 0;
		int remainder = 0;
		numerator = getNumeratorForFirstStepDivision();
		whole = getWhole(numerator, divisor);
		setCounterSpaces(numerator, whole);
		String lineDashes = getDash(getSize(dividend));
		resultDivision = getSpaces(counterSpases) + whole + "\n"
				+ lineDashes + "\n";
		while (true) {
			remainder = numerator % divisor;
			if (iteration >= dividendStr.length()) {
				doLastStepDivision(whole, remainder);
				break;
			}
			numerator = getNumeratorForOtherStep(remainder);
			setCounterSpaces(numerator, whole, remainder);
			resultDivision += getSpaces(counterSpases) + numerator + "\n";
			whole = getWhole(numerator, divisor);
			setCounterSpaces(numerator, whole);
			resultDivision += getSpaces(counterSpases) + whole + "\n"
					+ lineDashes + "\n";
			iteration++;
		}
	}

	/**
	 * Get a numerator (dividend) for the first step division.
	 * 
	 * @return integer number - part of the dividend that can be divided by the
	 *         divisor or dividend if it is less than the divisor.
	 */
	public static int getNumeratorForFirstStepDivision() {
		String numeratorStr = "";
		int numerator = 0;
		while (Math.abs(numerator) < Math.abs(divisor)
				&& iteration < dividendStr.length()) {
			numeratorStr += getDigitOfDivinend(iteration);
			numerator = Integer.parseInt(numeratorStr);
			iteration++;
		}
		return numerator;
	}

	/**
	 * Get a numerator (dividend) for the other steps division, except the first
	 * step. And if remainder and numerator equals to zero, counter of spaces is
	 * incremented by one.
	 * 
	 * @param remainder
	 *            - integer number, remainder as a result from a previous step
	 *            division.
	 * 
	 * @return integer number - part of the dividend with the remainder.
	 */
	public static int getNumeratorForOtherStep(int remainder) {
		String numeratorStr = remainder + "";
		int numerator = 0;
		while (true) {
			numeratorStr += getDigitOfDivinend(iteration);
			numerator = Integer.parseInt(numeratorStr);
			if (remainder == 0 && numerator == 0) {
				counterSpases++;
			}
			if (Math.abs(numerator) >= Math.abs(divisor)
					|| iteration >= dividendStr.length() - 1) {
				break;
			}
			iteration++;
		}
		return numerator;
	}

	/**
	 * Method gives a digit of the dividend on an index.
	 * 
	 * @param index
	 *            - character index in a dividend.
	 * @return digit as string of the dividend.
	 */
	public static String getDigitOfDivinend(int index) {
		return dividendStr.substring(index, index + 1);
	}

	/**
	 * In this method the whole part is calculated in division.
	 * 
	 * @param numerator
	 *            - the intermediate dividend.
	 * @param divisor
	 *            - number divider which was entered by user.
	 * @return integer number - whole part in division.
	 */
	public static int getWhole(int numerator, int divisor) {
		return numerator - (numerator % divisor);
	}

	/**
	 * This method is executed if selected all the digits of dividend and
	 * continue the division is no possible. In this method calculated the
	 * number of spaces and saved the remainder of a string.
	 * 
	 * @param whole
	 *            - whole part in division.
	 * @param remainder
	 *            - remainder of a division.
	 */
	public static void doLastStepDivision(int whole, int remainder) {
		setCounterSpaces(whole, remainder);
		resultDivision += getSpaces(counterSpases) + remainder;
	}

	/**
	 * In this method sets counter spaces, depending on the number of digits in
	 * the two numbers. If the counter is zero and dividend number is negative
	 * the counter is incremented by one.
	 * 
	 * @param firstNumber
	 *            - the number of a division.
	 * @param secondNumber
	 *            - the number of a division.
	 */
	public static void setCounterSpaces(int firstNumber, int secondNumber) {
		if (counterSpases == 0 && dividend < 0) {
			counterSpases++;
		}
		counterSpases += Math.abs(getSize(firstNumber) - getSize(secondNumber));
	}

	/**
	 * In this method sets counter spaces, depending on the number of digits in
	 * the whole part and remainder considering numerator. If the remainder is
	 * zero and numerator not zero the counter is incremented by one.
	 * 
	 * @param numerator
	 *            - the intermediate dividend.
	 * @param whole
	 *            - whole part in division.
	 * @param remainder
	 *            - remainder of a division.
	 */
	public static void setCounterSpaces(int numerator, int whole, int remainder) {
		if (remainder == 0 && numerator != 0) {
			counterSpases++;
		}
		setCounterSpaces(whole, remainder);
	}

	/**
	 * Output to the console a head of the column division.
	 * 
	 * @param dividend
	 *            - number divisible.
	 * @param divisor
	 *            - number on which division will be done.
	 */
	public static void printHeaderColumnToConsole(int dividend, int divisor) {
		System.out.println(String.format("%d|%d", dividend, divisor));
		int quotient = dividend / divisor;
		int sizeDividend = getSize(dividend);
		int sizedivisor = getSize(divisor);
		System.out.println(getSpaces(sizeDividend) + "|"
				+ getDash(sizedivisor, getSize(quotient)));
		System.out.println(getSpaces(sizeDividend) + "|" + quotient);
	}

	/**
	 * Output to the console results of the column division.
	 * 
	 * @param columnDivision
	 *            - string, the result of long division.
	 */
	public static void printBodyColumnToConsole(String columnDivision) {
		System.out.println(columnDivision);
	}

	/**
	 * Do verification - is the input number is correctly. divisor must not be
	 * zero.
	 * 
	 * @param divisor
	 *            - any integer number.
	 * @return true if the number is not zero, false otherwise.
	 */
	public static boolean isDivisorZero(int divisor) {
		return divisor == 0;
	}

	/**
	 * Calculates the number of digits in a number.
	 * 
	 * @param number
	 *            - any integer number.
	 * @return number of digits in a number.
	 */
	public static int getSize(int number) {
		return String.valueOf(number).length();
	}

	/**
	 * Generates a string of dashes.
	 * 
	 * @param size
	 *            - the number of dashes in a string.
	 * @return string of dashes.
	 */
	public static String getDash(int size) {
		return String.format("%0" + size + "d", 0).replace("0", "-");
	}

	/**
	 * Generates a string of dashes large size of the two sets.
	 * 
	 * @param firstSize
	 *            - first number of dashes in a string.
	 * @param secondSize
	 *            - second number of dashes in a string.
	 * @return string of dashes.
	 */
	public static String getDash(int firstSize, int secondSize) {
		int size = Math.max(firstSize, secondSize);
		return String.format("%0" + size + "d", 0).replace("0", "-");
	}

	/**
	 * Generates a string of spaces.
	 * 
	 * @param number
	 *            - the number of spaces in a string.
	 * @return string of spaces.
	 */
	public static String getSpaces(int number) {
		return (number == 0) ? "" : String.format("%" + number + "s", "");
	}

	/**
	 * Generates a string of spaces whose length equals the difference between
	 * two numbers.
	 * 
	 * @param firstNumber
	 *            - first number of spaces in a string.
	 * @param secondNumber
	 *            - second number of spaces in a string.
	 * @return string of spaces.
	 */
	public static String getSpaces(int firstNumber, int secondNumber) {
		int number = Math.abs(firstNumber - secondNumber);
		return (number == 0) ? "" : String.format("%" + number + "s", "");
	}
}

